package yiran.agift.manager;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.io.BukkitObjectInputStream;
import org.bukkit.util.io.BukkitObjectOutputStream;
import yiran.agift.AGift;
import yiran.agift.tool.Base64Util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class GiftManager {
    private final DatabaseManager dbManager;
    private final Map<Integer, String> giftNameCache = new HashMap<>();

    public GiftManager() {
        dbManager = new DatabaseManager();
    }

    /**
     * 创建一个新的礼包并将其添加到数据库中
     *
     * @param name 礼包的名称，用于在数据库中标识礼包
     * @param items 礼包包含的物品数组，这些物品将被序列化后存储
     * @return 返回数据库生成的礼包ID，如果创建失败则返回-1
     * <p>
     * 此方法负责将提供的礼包名称和物品序列化后，插入到数据库的gifts表中
     * 它还处理了SQL约束失败的情况，例如礼包名称已存在的情况
     */
    public int createGift(String name, ItemStack[] items) {
        // SQL语句，用于插入新的礼包数据
        String sql = "INSERT INTO gifts (name, items) VALUES (?, ?)";
        // 将物品数组序列化为字符串
        String serialized = serializeItems(items);
        try (
            // 获取数据库连接
            Connection conn = dbManager.getConnection();
            // 准备SQL语句执行对象，同时设置返回生成的键值
            PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)
        ) {
            // 设置SQL语句的参数
            pstmt.setString(1, name);
            pstmt.setString(2, serialized);
            // 执行更新操作
            pstmt.executeUpdate();
            // 获取生成的主键值
            ResultSet rs = pstmt.getGeneratedKeys();
            // 检查是否有生成的主键值
            if (rs.next()) {
                // 获取新的ID
                int newId = rs.getInt(1);
                // 将新的礼包ID和名称添加到缓存中
                giftNameCache.put(newId, name);
                // 返回新的礼包ID
                return newId;
            }
        } catch (SQLException e) {
            // 处理SQL异常
            if (e.getMessage().contains("UNIQUE constraint failed")) {
                // 如果是由于唯一约束失败，记录 severe 日志
                AGift.getInstance().getLogger().severe("礼包名称已存在: " + name);
            } else {
                // 其他SQL错误，记录 severe 日志
                AGift.getInstance().getLogger().severe("数据库错误: " + e.getMessage());
            }
            // 打印异常堆栈跟踪
            e.printStackTrace();
        }
        // 如果执行到这，表示礼包创建失败，返回-1
        return -1;
    }


    /**
     * 编辑指定ID的礼包名称和物品
     *
     * @param id 礼包的唯一标识符
     * @param newName 新的礼包名称
     * @param newItems 新的礼包物品数组
     * @return 如果更新成功则返回true，否则返回false
     */
    public boolean editGift(int id, String newName, ItemStack[] newItems) {
        // SQL语句用于更新数据库中的礼包信息
        String sql = "UPDATE gifts SET name = ?, items = ? WHERE id = ?";
        // 将新的礼包物品数组序列化为字符串
        String serialized = serializeItems(newItems);

        try (Connection conn = dbManager.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            // 设置PreparedStatement参数
            pstmt.setString(1, newName);
            pstmt.setString(2, serialized);
            pstmt.setInt(3, id);

            // 执行更新操作
            int rowsUpdated = pstmt.executeUpdate();
            // 如果更新成功，更新缓存并返回true
            if (rowsUpdated > 0) {
                giftNameCache.put(id, newName);
                return true;
            }
        } catch (SQLException e) {
            // 记录数据库操作失败的错误信息
            AGift.getInstance().getLogger().severe("编辑礼包失败: " + e.getMessage());
            e.printStackTrace();
        }
        // 如果更新失败，返回false
        return false;
    }

    /**
     * 根据礼物ID删除礼物
     * 此方法执行一个SQL删除操作，从数据库中移除指定ID的礼物记录
     * 它还负责从缓存中移除对应的礼物名称，以保持数据一致性
     *
     * @param id 要删除的礼物的ID
     */
    public void deleteGift(int id) {
        // 定义SQL语句，用于从gifts表中删除指定ID的记录
        String sql = "DELETE FROM gifts WHERE id = ?";
        try (
                // 获取数据库连接
                Connection conn = dbManager.getConnection();
                // 准备SQL语句执行对象
                PreparedStatement pstmt = conn.prepareStatement(sql)
        ) {
            // 设置SQL语句中的参数值
            pstmt.setInt(1, id);
            // 执行SQL语句
            pstmt.executeUpdate();
            // 从缓存中移除已删除礼物的名称
            giftNameCache.remove(id);
        } catch (SQLException e) {
            // 打印异常信息
            e.printStackTrace();
        }
    }

    /**
     * 根据礼物ID获取礼物包含的物品堆
     * 此方法通过查询数据库中与给定ID关联的物品信息，来检索和反序列化这些物品
     *
     * @param id 礼物的唯一标识符
     * @return 返回一个包含有效物品堆的数组，如果没有找到对应的礼物或出现异常，则返回null
     */
    public ItemStack[] getGift(int id) {
        // 定义SQL查询语句，用于从数据库中获取与给定ID对应的物品信息
        String sql = "SELECT items FROM gifts WHERE id = ?";
        try (Connection conn = dbManager.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setInt(1, id);
            ResultSet rs = pstmt.executeQuery();
            if (rs.next()) {
                // 从查询结果中反序列化物品堆数组
                ItemStack[] items = deserializeItems(rs.getString("items"));
                // 过滤掉数组中的空项或空气物品，返回有效的物品堆数组
                return Arrays.stream(items)
                        .filter(item -> item != null && item.getType() != Material.AIR)
                        .toArray(ItemStack[]::new);
            }
        } catch (SQLException e) {
            // 打印异常信息，便于调试和日志记录
            e.printStackTrace();
        }
        // 如果查询失败或发生异常，返回null
        return null;
    }

    /**
     * 序列化物品堆数组
     * 该方法将物品堆数组转换为Base64编码的字符串，以便于存储或传输
     * 如果输入的物品堆数组为null或空，则返回一个表示空数组的Base64编码字符串
     * 如果在序列化过程中发生错误，则返回一个空字符串或相应的错误信息
     *
     * @param items 物品堆数组，可以为空
     * @return 序列化后的Base64编码字符串
     */
    private String serializeItems(ItemStack[] items) {
        // 检查物品堆数组是否为null或空，如果是，则序列化一个空数组
        if (items == null || items.length == 0) {
            try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                 BukkitObjectOutputStream bukkitStream = new BukkitObjectOutputStream(outputStream)) {
                // 写入数组长度为0，表示空数组
                bukkitStream.writeInt(0);
                // 返回表示空数组的Base64编码字符串
                return Base64Util.encode(outputStream.toByteArray());
            } catch (IOException e) {
                // 如果发生IO错误，返回一个空的Base64编码字符串
                return Base64Util.encode(new byte[0]);
            }
        }

        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
             BukkitObjectOutputStream bukkitStream = new BukkitObjectOutputStream(outputStream)) {
            // 写入数组长度，用于反序列化时重建数组
            bukkitStream.writeInt(items.length);
            // 遍历物品堆数组，逐个进行序列化
            for (ItemStack item : items) {
                bukkitStream.writeObject(item);
            }
            // 返回序列化后的Base64编码字符串
            return Base64Util.encode(outputStream.toByteArray());
        } catch (IOException e) {
            // 如果在序列化过程中发生IO错误，记录错误信息并返回一个空字符串
            AGift.getInstance().getLogger().severe("序列化物品失败: " + e.getMessage());
            return "";
        }
    }

    /**
     * 将字符串形式的物品数据反序列化为ItemStack数组
     * 该方法使用Base64解码和对象流来解析物品数据
     *
     * @param data 物品数据的字符串表示，经过Base64编码
     * @return 反序列化后的ItemStack数组如果数据为空或无效，则返回空数组
     */
    private ItemStack[] deserializeItems(String data) {
        // 检查输入数据是否为空或无效
        if (data == null || data.isEmpty()) return new ItemStack[0];

        // 使用Base64解码数据
        byte[] bytes = Base64Util.decode(data);

        // 使用输入流反序列化物品数据
        try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
             BukkitObjectInputStream bukkitStream = new BukkitObjectInputStream(inputStream)) {

            // 读取物品数组的长度
            int length = bukkitStream.readInt();

            // 创建ItemStack数组
            ItemStack[] items = new ItemStack[length];

            // 逐个读取并添加物品到数组中
            for (int i = 0; i < length; i++) {
                items[i] = (ItemStack) bukkitStream.readObject();
            }

            // 返回反序列化后的物品数组
            return items;
        } catch (IOException | ClassNotFoundException e) {
            // 捕获异常并打印堆栈跟踪
            e.printStackTrace();

            // 如果发生异常，返回空数组
            return new ItemStack[0];
        }
    }

    /**
     * 根据礼包ID获取礼包名称
     * 首先尝试从缓存中获取礼包名称，如果缓存中不存在，则通过数据库查询
     * 如果查询成功，更新缓存并返回名称；如果查询失败或出现异常，返回默认名称
     *
     * @param id 礼包ID，用于查询缓存和数据库
     * @return 礼包名称，如果查询失败或出现异常，则返回"未知礼包"
     */
    public String getGiftName(int id) {
        // 检查缓存中是否存在该ID的礼包名称
        if (giftNameCache.containsKey(id)) {
            return giftNameCache.get(id);
        }
        // SQL查询语句，用于从数据库中获取指定ID的礼包名称
        String sql = "SELECT name FROM gifts WHERE id = ?";
        try (Connection conn = dbManager.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setInt(1, id);
            ResultSet rs = pstmt.executeQuery();
            // 处理查询结果，如果查询成功，更新缓存并返回名称
            if (rs.next()) {
                String name = rs.getString("name");
                giftNameCache.put(id, name);
                return name;
            }
        } catch (SQLException e) {
            // 打印异常信息，以便于调试和日志记录
            e.printStackTrace();
        }
        // 如果查询失败或出现异常，返回默认名称
        return "未知礼包";
    }

    /**
     * 关闭数据库连接
     */
    public void close() {
        dbManager.close();
    }
}
